package com.jcj.jcjsharding.mybatis;

import com.jcj.jcjsharding.engine.ShardingContext;
import com.jcj.jcjsharding.engine.ShardingResult;
import com.jcj.jcjsharding.demo.model.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.mapper.MapperFactoryBean;

import java.lang.reflect.Proxy;

@Slf4j
public class ShardingMapperFactoryBean<T> extends MapperFactoryBean<T> {

    public ShardingMapperFactoryBean() {

    }

    public ShardingMapperFactoryBean(Class<T> mapperInterface) {
        super(mapperInterface);
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public T getObject() throws Exception {
        Object proxy = super.getObject();
        SqlSession session = getSqlSession();
        Configuration configuration = session.getConfiguration();
        Class<?> clazz = getMapperInterface();
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, (p, method, args) -> {
            //获取分片

            String mapperId = clazz.getName() + "." + method.getName();
            MappedStatement mappedStatement = configuration.getMappedStatement(mapperId);
            BoundSql boundSql = mappedStatement.getBoundSql(args[0]);

            //执行sql之前，设置ShardingContext，用于后面选择哪个数据源
            Object parameterObject = boundSql.getParameterObject();
            if (parameterObject instanceof User user) {
                ShardingContext.set(new ShardingResult(user.getId() % 2 == 0 ? "ds0" : "ds1"));
            } else if (parameterObject instanceof Integer id) {
                ShardingContext.set(new ShardingResult(id % 2 == 0 ? "ds0" : "ds1"));
            }
            log.info("》》mapperId:{},sql:{}，选择：{}", mapperId, boundSql.getSql(), ShardingContext.get());

            //反射调用原方法
            return method.invoke(proxy, args);
        });

    }


}
